﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UndoRedoManager.cs" >
//   J. Prinz
// </copyright>
// <summary>
//   Interface for Undo/Redo Event - Management
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace AutomationMLMapper.Utilities
{
    using System.Collections.Generic;

    /// <summary>
    ///   The UndoRedo Manager implements Undo Redo for Objects which implement the IUndoEvent Interface
    /// </summary>
    public class UndoRedoManager
    {
        // Undo Stack

        // Redo Stack
        #region Fields

        /// <summary>
        ///   The redo events collection.
        /// </summary>
        private readonly Stack<IUndoEvent> redoEventsCollection = new Stack<IUndoEvent>();

        /// <summary>
        ///   The undo events collection.
        /// </summary>
        private readonly Stack<IUndoEvent> undoEventsCollection = new Stack<IUndoEvent>();

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UndoRedoManager"/> class. 
        /// </summary>
        public UndoRedoManager()
        {
            this.IsActive = false;
            this.IsEnabled = false;
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///   Redo Stack contains some data
        /// </summary>
        public bool CanRedo
        {
            get
            {
                return this.redoEventsCollection.Count > 0;
            }
        }

        /// <summary>
        ///   Undo Stack contains some data
        /// </summary>
        public bool CanUndo
        {
            get
            {
                return this.undoEventsCollection.Count > 0;
            }
        }

        /// <summary>
        ///   During an undo or Redo, this Property is true. Porperty Changes, which are raised 
        ///   from Undo and Redo Actions should not be reported to the UndoRedo Manager again,
        ///   this would result in an infinite loop. Before Undo Redo Events are reported to the
        ///   UndoRedo Manager, this Property should be checked.
        /// </summary>
        public bool IsActive { get; set; }

        /// <summary>
        ///   Events can only be registred, if the stack is enabled
        /// </summary>
        public bool IsEnabled { get; set; }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        ///   All Undo- and Redo Events are cleared
        /// </summary>
        public void ClearAll()
        {
            this.redoEventsCollection.Clear();
            this.undoEventsCollection.Clear();
        }

        /// <summary>
        ///   <para> All Redo events are cleared. </para>
        ///   <para> Reason: If the user performes an undo and does then a change, the redo stack should be cleared. </para>
        /// </summary>
        public void ClearRedo()
        {
            this.redoEventsCollection.Clear();
        }

        /// <summary>
        /// Report a New UndoRedo-Event if the UndoRedo Manger is not active
        /// </summary>
        /// <param libraryName="eventObject">
        /// The Object to insert
        /// </param>
        /// <returns>
        /// True if Inserted 
        /// </returns>
        public bool InsertObjectforUndoRedo(IUndoEvent eventObject)
        {
            if (!this.IsActive && this.IsEnabled)
            {
                this.undoEventsCollection.Push(eventObject);
                this.redoEventsCollection.Clear();
                return true;
            }

            return false;
        }

        /// <summary>
        /// Perform Redo for a number of events
        /// </summary>
        /// <param libraryName="numberOfRedoEvents">
        /// Number of Events 
        /// </param>
        public void Redo(int numberOfRedoEvents)
        {
            this.IsActive = true;
            for (var i = 1; i <= numberOfRedoEvents; i++)
            {
                if (this.redoEventsCollection.Count == 0)
                {
                    return;
                }

                var poppedEvent = this.redoEventsCollection.Pop();
                this.undoEventsCollection.Push(poppedEvent);
                poppedEvent.Redo();
            }

            this.IsActive = false;
        }

        /// <summary>
        /// Perform Undo for a number of Events
        /// </summary>
        /// <param libraryName="numberOfUndoEvents">
        /// Number of Events 
        /// </param>
        public void Undo(int numberOfUndoEvents)
        {
            this.IsActive = true;
            for (var i = 1; i <= numberOfUndoEvents; i++)
            {
                if (this.undoEventsCollection.Count == 0)
                {
                    return;
                }

                var poppedEvent = this.undoEventsCollection.Pop();
                this.redoEventsCollection.Push(poppedEvent);
                poppedEvent.Undo();
            }

            this.IsActive = false;
        }

        #endregion
    }
}